#include "Batent.h"
using namespace Batent;

static vector<CmdObjectRef>& AppendCmdList(CmdObject *pCmd)
{
    static vector<CmdObjectRef> cmdobj_array;
    
    if (pCmd)
    {
        CmdObjectRef tmp(*pCmd);
        cmdobj_array.push_back(tmp);
    }

    return cmdobj_array;
}

static vector<CmdObjectRef>& GetCmdList(void)
{
    return AppendCmdList(NULL);
}

CmdObject::CmdObject(const string &cmd, bool global) : szCmd(cmd)
{
    if (global)
    {
        AppendCmdList(this);
    }
}

static int wrap_main(int argc, char **argv)
{
    vector<CmdObjectRef>::iterator Iter;
    CmdObject Obj(argv[0], false);
    CmdObjectRef Ref(Obj);
    vector<CmdObjectRef> &CmdList = GetCmdList();

    Iter = lower_bound(CmdList.begin(), CmdList.end(), Ref);
    if (*Iter == Ref)
    {
        vector<string>args(argv + 1, argv + argc);
        int paraNum = (int)args.size();

        if (paraNum < Iter->pCmd->minPara || paraNum > Iter->pCmd->maxPara)
        {
            error("Invalid param number, %d [%d-%d].\n", paraNum, Iter->pCmd->minPara, Iter->pCmd->maxPara);
            return ERR_INVALID_PARA;
        }

        return Iter->pCmd->run(args);
    }
    else
    {
        error("Command \"%s\" is NOT supported.\n", argv[0]);
        return ERR_NOT_SUPPORTED;
    }
}

static bool CmdCompare(const CmdObjectRef &a, const CmdObjectRef &b)
{
    if (a.pCmd == NULL || b.pCmd == NULL)
    {
        return false;
    }

    string::size_type Pos1 = a.pCmd->szCmd.find(".");
    string::size_type Pos2 = b.pCmd->szCmd.find(".");

    if (Pos1 == string::npos || Pos2 == string::npos)
    {
        return a.pCmd->szCmd < b.pCmd->szCmd;
    }
    
    return a.pCmd->szCmd.substr(0, Pos1) < b.pCmd->szCmd.substr(0, Pos2);
}

static void help(void)
{
    vector<CmdObjectRef> &CmdList = GetCmdList();
    
    print("Batent.exe   Bat extent tool version 1.0.0 %s\n", __DATE__);
    print("https://gitee.com/longpanda/Batent\n\n");
    print("Command list:\n");

    std::sort(CmdList.begin(), CmdList.end(), CmdCompare);

    for (size_t t = 0; t < CmdList.size(); t++)
    {
        print("  %-20s    %s\n", CmdList[t].pCmd->szCmd.c_str(), CmdList[t].pCmd->szDesc.c_str());
    }
    print("\n");
}

int main(int argc, char **argv)
{
    char szEnable[16] = { 0 };
    vector<CmdObjectRef> &CmdList = GetCmdList();
    
    // Check param
    if (argc <= 1)
    {
        error("Please input command.\n");
        help(); 
        return ERR_INVALID_PARA;
    }

    for (int i = 0; i < argc; i++)
    {
        if (argv[i] == NULL)
        {
            error("Param %d is NULL.\n", i);
            return ERR_INVALID_PARA;
        }
    }

    if (argc == 2 && strcmp(argv[1], "/?") == 0)
    {
        help();
        return ERR_SUCCESS;
    }

    // Log Flag
    if (GetEnvironmentVariable("BATENT_ERR_LOG_ENABLE", szEnable, sizeof(szEnable) - 1) > 0)
    {
        Util::setErrLogEnable(0 == strncmp(szEnable, "1", 1));
    }

    //sort command
    std::sort(CmdList.begin(), CmdList.end());

    errno = wrap_main(argc - 1, argv + 1);
    return errno;
}
